﻿using UnityEngine;
using System.Collections;
using System.Text.RegularExpressions;
[ExecuteInEditMode]
public class CodeGenerator : MonoBehaviour {
	public static CodeGenerator instance = null;//SingleTon

	private enum types{Texture,Int,Float,Vector,Color}
	private types Types;

	[HideInInspector]
	public string ShaderName;
	string Result;

	string BaseShader;


	#region MainFunctions
	void Update(){
		instance = this;
		//BaseShader = BaseShader.BaseReplace("ShaderName",ShaderName);
		BaseShader = @"Shader ShaderName
	Properties
	//Properties
	

	SubShader
    Tags
    LOD 100
	Pass
	{
	
	

	CGPROGRAM
	#pragma vertex vert
	#pragma fragment frag
	#include ""UnityCG.cginc""
    //#define Fixed3(value)  fixed3(value, value,value)
	//#define Fixed4(value)  fixed4(value, value,value,value)

	struct VertexInput {
    float4 vertex : POSITION;
	float2 xlv_TEXCOORD0 : TEXCOORD0;
    //float2 xlv_TEXCOORD1 : TEXCOORD1;
    //float3 xlv_TEXCOORD2 : TEXCOORD2;
    //float3 xlv_TEXCOORD3 : TEXCOORD3;
    //float4 tangent : TANGENT;
    float3 normal : NORMAL;
    //float4 xlv_COLOR : COLOR;
	//VertexInput
	};


	struct VertexOutput {
	float4 pos : SV_POSITION;
    UNITY_FOG_COORDS(1)
    //fixed4 xlv_COLOR : COLOR;
	float2 xlv_TEXCOORD0 : TEXCOORD0;
    //float2 xlv_TEXCOORD1 : TEXCOORD1;
    //float3 xlv_TEXCOORD2 : TEXCOORD2;
    //float3 xlv_TEXCOORD3 : TEXCOORD3;
    //fixed3 xlv_TEXCOORD4 : TEXCOORD4;
	//VertexOutput
	};

	//Variables

	//Functions



	VertexOutput vert (VertexInput v)
	{
	VertexOutput o;
	//o.pos = UnityObjectToClipPos (v.vertex);
	//o.xlv_TEXCOORD0 = TRANSFORM_TEX(v.xlv_TEXCOORD0, _MainTex);
	//VertexFactory
    UNITY_TRANSFER_FOG(o,o.vertex);
   
	return o;
	}


	fixed4 frag(VertexOutput i) : SV_Target
    {
    //fixed4 tmpvar_2 = tex2D(_MainTex, i.uv);
	UNITY_APPLY_FOG(i.fogCoord, tmpvar_2);
	//MainImage
	}
	ENDCG
	}
  }
Fallback ""Diffuse""
}
";

	}



	#endregion

	public static string GetValue(string str, string s, string e)
	{
		Regex rg = new Regex("(?<=(" + s + "))[.\\s\\S]*?(?=(" + e + "))", RegexOptions.Multiline | RegexOptions.Singleline);
		return rg.Match(str).Value;
	}

	public static Match GetMatch(string input,string s, string e)
	{
		Match rg = Regex.Match(input, "(?<=(" + s + "))[.\\s\\S]*?(?=(" + e + "))", RegexOptions.Multiline | RegexOptions.Singleline);
		return rg;
	}

	public object GlslConvert(string input)
	{
		var Vertex = GetMatch(input, "#ifdef VERTEX", "endif");
		//var VertexMain = Regex.Match(Vertex.Groups[0].Value, @"void\s+main[^\{]+\{([^}]+)\}", RegexOptions.Multiline | RegexOptions.Singleline);
		var VertexMain = GetMatch(Vertex.Groups[0].Value, @"main\s+\(+\)+\n{", "#");
		var Vertexfunctions = Regex.Match(Vertex.Groups[0].Value, @"(.*)(?=void main)", RegexOptions.Multiline | RegexOptions.Singleline);

		var FRAGMENT = GetMatch(input, "#ifdef FRAGMENT", "#endif");
		var FRAGMENTMain = Regex.Match(FRAGMENT.Groups[0].Value, @"void\s+main[^\{]+\{([^}]+)\}", RegexOptions.Multiline | RegexOptions.Singleline);
		
		var Fragmentfunctions = Regex.Match(FRAGMENT.Groups[0].Value, @"(.*)(?=void main)", RegexOptions.Multiline | RegexOptions.Singleline);
		var Tag = GetMatch(input, "Pass", "Program");

		var Name = GetMatch(input, "Shader", "Properties");

		//BaseReplace("ShaderName", ShaderName);
		BaseReplace("Tags", Tag.Value);
		BaseReplace("ShaderName", Name.Value);
		BaseReplace("//VertexFactory", Replace( VertexMain.Groups[0].Value, "xlv_TEXCOORD", "o.xlv_TEXCOORD"));

		if (!FRAGMENTMain.Value.Contains("gl_FragData"))
		{
			FRAGMENTMain = GetMatch(FRAGMENT.Value, @"main\s+\(+\)+\n{", "gl_FragData");
			BaseReplace("//MainImage", Replace( FRAGMENTMain.Value+" return col;", "xlv_TEXCOORD", "i.xlv_TEXCOORD"));

		}
		else
        {

		   BaseReplace("//MainImage", Replace(FRAGMENTMain.Groups[1].Value, "xlv_TEXCOORD", "i.xlv_TEXCOORD"));
		}

		BaseReplace("//Variables", Vertexfunctions.Groups[1].Value);
		BaseReplace("//Functions", Fragmentfunctions.Groups[1].Value);

		//var Properties = Regex.Match(input, @"Properties+\{([^}]+)\}", RegexOptions.Multiline | RegexOptions.Singleline);
		var Properties = GetMatch(input , "Properties", "SubShader");
		BaseReplace("//Properties", Properties.Value);

		//var mainImageComponents = Regex.Match(input, @"void\s+main\(\s*out\s*vec4\s*(.+?)\s*\,\s*in\s*vec2\s*(.+?)\s*\)", RegexOptions.Multiline | RegexOptions.Singleline);
		//var fragColor = mainImageComponents.Groups[1].Value;
		//var fragCoord = mainImageComponents.Groups[2].Value;

		//BaseReplace(fragColor, "fragColor");
		//BaseReplace(fragCoord, "fragCoord");



		//news
		BaseReplace(@"\=\s*vec3\(([^;,]+)\)", "= vec3($1,$1,$1)", RegexOptions.Multiline | RegexOptions.Singleline);
		BaseReplace(@"\=\s*vec4\(([^;,]+)\)", "= vec3($1,$1,$1,$1)", RegexOptions.Multiline | RegexOptions.Singleline);

		BaseReplace("vec|half|float", "fixed");
		BaseReplace("mix", "lerp");
		BaseReplace("iGlobalTime", "_Time.y");
		BaseReplace("fragColor =", "return");
		BaseReplace("fract", "frac");
		BaseReplace(@"ifixed(\d)", "fixed$1");//ifixed to fixed
		BaseReplace("texture2D", "tex2D");
		BaseReplace("tex2DLod", "tex2Dlod");
		BaseReplace("refrac", "refract");
		BaseReplace("iChannel0", "_MainTex");
		BaseReplace("iChannel1", "_SecondTex");
		BaseReplace("iChannel2", "_ThirdTex");
		BaseReplace("iChannel3", "_FourthTex");
		//BaseReplace( "fragCoord", "i.vertex");
		BaseReplace(@"iResolution.((x|y){1,2})?", "1");
		BaseReplace(@"fragCoord.xy / iResolution.xy", "i.uv");
		BaseReplace(@"fragCoord(.xy)?", "i.uv");
		BaseReplace(@"iResolution(\.(x|y){1,2})?", "1");

		BaseReplace("iMouse", "_iMouse");
		BaseReplace("mat2", "fixed2x2");
		BaseReplace("mat3", "fixed3x3");
		BaseReplace("mat4", "fixed4x4");
		//BaseReplace( @"(m)\*(p)", "mul($1,$2)");
		BaseReplace("mod", "fmod");
		BaseReplace(@"for\(", "[unroll(100)]\nfor(");
		BaseReplace("iTime", "_Time.y");
		BaseReplace(@"(tex2Dlod\()([^,]+\,)([^)]+\)?[)]+.+(?=\)))", "$1$2float4($3,0)");
		BaseReplace(@"fixed4\(([^(,]+?)\)", "fixed4($1,$1,$1,$1)");
		BaseReplace(@"fixed3\(([^(,]+?)\)", "fixed3($1,$1,$1)");
		BaseReplace(@"fixed2\(([^(,]+?)\)", "fixed2($1,$1)");
		BaseReplace(@"tex2D\(([^,]+)\,\s*fixed2\(([^,].+)\)\,(.+)\)", "tex2Dlod($1,fixed4($2,fixed2($3,$3)))");//when vec3 col = texture( iChannel0, vec2(uv.x,1.0-uv.y), lod ).xyz; -> https://www.shadertoy.com/view/4slGWn
																											   //BaseReplace( @"#.+","");
		BaseReplace(@"texelFetch", "tex2D");//badan bokonesh texlod
		BaseReplace(@"atan\(([^,]+?)\,([^,]+?)\)", "atan2($2,$1)");//badan bokonesh texlod
																   //BaseReplace( "([*+\\/-])\\s*(pi|PI)", "$13.14159265359");

		BaseReplace("gl_FragCoord", "((i.screenCoord.xy/i.screenCoord.w)*_ScreenParams.xy)");
		//BaseReplace( @"(.+\s*)(\*\=)\s*([^ ;*+\/]+)", "$1 = mul($1,$3)");

		BaseReplace("mediump fixed", "half");
		BaseReplace("lowp fixed", "fixed");
		BaseReplace("highp fixed", "float");
		BaseReplace("attribute", "");
		BaseReplace("fixed4 _glesVertex;", "");
		BaseReplace("fixed4 _glesColor;;", "");
		BaseReplace("fixed4 _glesMultiTexCoord0;", "");
		BaseReplace("varying", "");
		BaseReplace("float4 unity_Scale", "");
		
		//BaseReplace(@"\_+glesFragData+\[+\0+\]\s+\=", "return");
		BaseShaderReplace("_glesFragData[0] =", "return");
		//BaseReplace("gl_FragData[0]", "return");
		BaseReplace(@"_glesFragData[0] =", "return");
		BaseReplace("uniform", "");
		BaseReplace("float4x4 glstate_matrix_mvp;", "");
		BaseReplace("#ifdef VERTEX", "");
		BaseReplace("#endif", "");
		BaseReplace("#ifdef FRAGMENT", "");
		
		BaseReplace(@"(glstate_matrix_mvp * _glesVertex)", "UnityObjectToClipPos (v.vertex)");
		//BaseReplace(@"\(+_Object2World\s+\*)", "mul(unity_ObjectToWorld ,)");
		BaseShaderReplace(@"(_Object2World *", "mul(unity_ObjectToWorld ,");

		BaseReplace("_glesVertex", "v.vertex");
		BaseReplace("texture2DProj", "tex2Dproj");
		
		BaseReplace("_glesColor", "v.xlv_COLOR");
		BaseReplace("gl_Position", "o.pos");
		BaseReplace("_glesNormal", "v.normal");
		BaseReplace("float4 _Time;", "");
		BaseReplace(@"(unity_ObjectToWorld * v.vertex)", "mul(unity_ObjectToWorld , v.vertex)");
		//BaseReplace(@"((_glesMultiTexCoord0.xy * _MainTex_ST.xy) + _MainTex_ST.zw)", "TRANSFORM_TEX(v.uv, _MainTex)");

		BaseReplace("_glesMultiTexCoord0.xy", "v.xlv_TEXCOORD0");
		BaseReplace("_glesMultiTexCoord0", "v.xlv_TEXCOORD0");
		BaseReplace("_glesMultiTexCoord1", "v.xlv_TEXCOORD1");
		BaseReplace("float2 xlv_TEXCOORD0", "");
		BaseReplace("float2 xlv_TEXCOORD1", "");
		BaseReplace("float3 xlv_TEXCOORD2", "");
		BaseReplace("float3 xlv_TEXCOORD3", "");
		BaseReplace("fixed4 v.xlv_COLOR;", "");
		BaseReplace("half2 xlv_TEXCOORD0;", "");
		BaseReplace("float4 xlv_COLOR0;", "");
		BaseReplace("fixed4 v.xlv_COLOR0;", "");
		BaseReplace("float4 xlv_TEXCOORD0;", "");
		BaseReplace("fixed3 v.normal;", "");
		BaseReplace("float xlv_TEXCOORD;", "");
		BaseReplace("float3 xlv_TEXCOORD1;", "");
		BaseReplace(@"\(+glstate_matrix_mvp\s+\*", "mul(UNITY_MATRIX_MVP ,");

        if (BaseShader.Contains("xlv_COLOR"))
        {
			BaseReplace("//float4 xlv_COLOR", "float4 xlv_COLOR");
		}

		if (BaseShader.Contains("xlv_TEXCOORD1"))
		{
			BaseReplace("//float3 xlv_TEXCOORD1", "float3 xlv_TEXCOORD1");
		}

		if (BaseShader.Contains("xlv_TEXCOORD2"))
		{
			BaseReplace("//float3 xlv_TEXCOORD2", "float3 xlv_TEXCOORD2");
		}

		if (BaseShader.Contains("xlv_TEXCOORD3"))
		{
			BaseReplace("//float3 xlv_TEXCOORD3", "float3 xlv_TEXCOORD3");
		}

		if (BaseShader.Contains("xlv_TEXCOORD4"))
		{
			BaseReplace("//float3 xlv_TEXCOORD4", "float3 xlv_TEXCOORD4");
		}


		//if (BaseShader.Contains("_MainTex"))
		//{
		//	Decelaration("MainTex", types.Texture);
		//}
		//if (BaseShader.Contains("_SecondTex"))
		//{
		//	Decelaration("SecondTex", types.Texture);
		//}
		//if (BaseShader.Contains("_ThirdTex"))
		//{
		//	Decelaration("ThirdTex", types.Texture);
		//}
		//if (BaseShader.Contains("_FourthTex"))
		//{
		//	Decelaration("FourthTex", types.Texture);
		//}

		//if (BaseShader.Contains("iMouse"))
		//{
		//	Decelaration("iMouse", types.Vector);
		//}

		//if (BaseShader.Contains("iDate"))
		//{
		//	Decelaration("iDate", types.Vector);
		//}


		return BaseShader;
	}


	public object Convert(string input)
	{
		var mainImage = Regex.Match(input, @"void\s+mainImage[^\{]+\{([^}]+)\}",RegexOptions.Multiline | RegexOptions.Singleline);
		var functions = Regex.Match(input, @"(.*)(?=void mainImage)",RegexOptions.Multiline | RegexOptions.Singleline);
		print ( mainImage.Groups [1].Value);
		BaseReplace("ShaderName",ShaderName);
		BaseReplace ("//MainImage", mainImage.Groups [1].Value);
		BaseReplace ("//Functions", functions.Groups[1].Value);



		var mainImageComponents = Regex.Match(input, @"void\s+mainImage\(\s*out\s*vec4\s*(.+?)\s*\,\s*in\s*vec2\s*(.+?)\s*\)",RegexOptions.Multiline | RegexOptions.Singleline);
		var fragColor = mainImageComponents.Groups [1].Value;
		var fragCoord = mainImageComponents.Groups [2].Value;

		BaseReplace(fragColor,"fragColor");
		BaseReplace(fragCoord,"fragCoord");



		//news
		BaseReplace( @"\=\s*vec3\(([^;,]+)\)", "= vec3($1,$1,$1)",RegexOptions.Multiline | RegexOptions.Singleline);
		BaseReplace( @"\=\s*vec4\(([^;,]+)\)", "= vec3($1,$1,$1,$1)",RegexOptions.Multiline | RegexOptions.Singleline);

		BaseReplace( "vec|half|float", "fixed");
		BaseReplace( "mix", "lerp");
		BaseReplace( "iGlobalTime", "_Time.y");
		BaseReplace( "fragColor =", "return");
		BaseReplace( "fract", "frac");
		BaseReplace( @"ifixed(\d)", "fixed$1");//ifixed to fixed
		BaseReplace( "texture", "tex2D");
		BaseReplace( "tex2DLod", "tex2Dlod");
		BaseReplace( "refrac", "refract");
		BaseReplace( "iChannel0", "_MainTex");
		BaseReplace( "iChannel1", "_SecondTex");
		BaseReplace( "iChannel2", "_ThirdTex");
		BaseReplace( "iChannel3", "_FourthTex");
		//BaseReplace( "fragCoord", "i.vertex");
		BaseReplace (@"iResolution.((x|y){1,2})?", "1");
		BaseReplace( @"fragCoord.xy / iResolution.xy", "i.uv");
		BaseReplace( @"fragCoord(.xy)?", "i.uv");
		BaseReplace( @"iResolution(\.(x|y){1,2})?", "1");

		BaseReplace( "iMouse", "_iMouse");
		BaseReplace( "mat2", "fixed2x2");
		BaseReplace( "mat3", "fixed3x3");
		BaseReplace( "mat4", "fixed4x4");
		//BaseReplace( @"(m)\*(p)", "mul($1,$2)");
		BaseReplace( "mod", "fmod");
		BaseReplace( @"for\(", "[unroll(100)]\nfor(");
		BaseReplace( "iTime", "_Time.y");
		BaseReplace( @"(tex2Dlod\()([^,]+\,)([^)]+\)?[)]+.+(?=\)))", "$1$2float4($3,0)");
		BaseReplace( @"fixed4\(([^(,]+?)\)", "fixed4($1,$1,$1,$1)");
		BaseReplace( @"fixed3\(([^(,]+?)\)", "fixed3($1,$1,$1)");
		BaseReplace( @"fixed2\(([^(,]+?)\)", "fixed2($1,$1)");
		BaseReplace( @"tex2D\(([^,]+)\,\s*fixed2\(([^,].+)\)\,(.+)\)", "tex2Dlod($1,fixed4($2,fixed2($3,$3)))");//when vec3 col = texture( iChannel0, vec2(uv.x,1.0-uv.y), lod ).xyz; -> https://www.shadertoy.com/view/4slGWn
		//BaseReplace( @"#.+","");
		BaseReplace( @"texelFetch","tex2D");//badan bokonesh texlod
		BaseReplace( @"atan\(([^,]+?)\,([^,]+?)\)","atan2($2,$1)");//badan bokonesh texlod
		//BaseReplace( "([*+\\/-])\\s*(pi|PI)", "$13.14159265359");

		BaseReplace( "gl_FragCoord", "((i.screenCoord.xy/i.screenCoord.w)*_ScreenParams.xy)");
		//BaseReplace( @"(.+\s*)(\*\=)\s*([^ ;*+\/]+)", "$1 = mul($1,$3)");

		if(BaseShader.Contains("_MainTex")){
			Decelaration ("MainTex", types.Texture);
		}
		if(BaseShader.Contains("_SecondTex")){
			Decelaration ("SecondTex", types.Texture);
		}
		if(BaseShader.Contains("_ThirdTex")){
			Decelaration ("ThirdTex", types.Texture);
		}
		if(BaseShader.Contains("_FourthTex")){
			Decelaration ("FourthTex", types.Texture);
		}

		if (BaseShader.Contains ("iMouse")) {
			Decelaration ("iMouse", types.Vector);
		}
		if (BaseShader.Contains ("iDate")) {
			Decelaration ("iDate", types.Vector);
		}


		return BaseShader;
	}


	void Decelaration(string name,types type){

		string VariableType = "";
		string Initialize = "";
		string CorrespondingVariable = "";

		switch (type) {
		case types.Int:
			VariableType = "int";
			CorrespondingVariable = "int";
			Initialize = "0";
			break;
		case types.Float:
			VariableType = "float";
			CorrespondingVariable = "float";
			Initialize = "0";
			break;
		case types.Texture:
			VariableType = "2D";
			CorrespondingVariable = "sampler2D";
			Initialize = @"""white"" {}";

			break;
		case types.Color:
			VariableType = "Color";
			CorrespondingVariable = "float4";
			Initialize = "(0,0,0,0)";
			break;
		case types.Vector:
			VariableType = "Vector";
			CorrespondingVariable = "float4";
			Initialize = "(0,0,0,0)";
			break;
		default:
			VariableType = "int";
			CorrespondingVariable = "int";
			Initialize = "0";
			break;
		}
		CorrespondingVariable += " _" + name + ";";//for example sampler2D _MainTex;

		string Properties = @"_name (""name"", type) = initialize";
		Properties = Regex.Replace (Properties, "name", name);
		Properties = Regex.Replace (Properties, "type", VariableType);
		Properties = Regex.Replace (Properties, "initialize", Initialize);
		BaseReplace ( "//Properties", Properties);
		BaseReplace ( "//Variables", "$0\n"+CorrespondingVariable);
	}



	void BaseReplace(string pattern,string replacement){
		BaseShader = Regex.Replace(BaseShader,pattern,replacement);
	}

	void BaseShaderReplace(string pattern, string replacement)
	{
		BaseShader = BaseShader .Replace(pattern ,replacement);
	}

	void BaseReplace(string pattern,string replacement,RegexOptions options){
		BaseShader = Regex.Replace(BaseShader,pattern,replacement,options);
	}

	string Replace(string input, string pattern, string replacement)
	{

		return Regex.Replace(input, pattern, replacement);

	}
}
